

#import "UDMOctogris_UI.h"
#import "UDMOctogris_LevelView.h"
#define NSBOX_HEIGHT 800

//=================================================================================
@interface UDMOctogris_Slider : NSSlider
{
	UDMOctogris_UI *mUI;
	int mType;
	float mThickness;
//	BOOL mSetSliderToDefault;
}
- (void) setUI:(UDMOctogris_UI*)ui type:(int)t;
@end
@implementation UDMOctogris_Slider
- (void) setUI:(UDMOctogris_UI*)ui type:(int)t
{
	mUI = ui;
	mType = t;
	mThickness = [[self cell] knobThickness];
}
// NSSlider starts a second run loop in mouseDown: we don't want that as it breaks
// automation writing in Digital performer
- (void)mouseDown:(NSEvent*)theEvent
{
	
	if (mUI) { [mUI sliderNotify:self type:mType event:kAudioUnitEvent_BeginParameterChangeGesture]; printf("begin %i\n", mType); }
	[self mouseDragged:theEvent];
}
- (void)mouseDragged:(NSEvent *)theEvent
{
	
	NSPoint p = [self convertPoint:[theEvent locationInWindow] fromView:nil];
	NSRect b = [self bounds];
	b.origin.x += mThickness / 2;
	b.size.width -= mThickness;
	double min = [self minValue];
	double max = [self maxValue];
	double x = (p.x - b.origin.x) / b.size.width;
	[self setDoubleValue:min + x * (max - min)];
	
	id t = [self target];
	SEL a = [self action];
	if (t && a) [t performSelector:a withObject:self];
}
- (void)mouseUp:(NSEvent *)theEvent
{
	unsigned int flags = [theEvent modifierFlags];
	if(flags & NSAlternateKeyMask)
		mSetSliderToDefault = TRUE;
	[self mouseDragged:theEvent];
	
	if (mUI) { [mUI sliderNotify:self type:mType event:kAudioUnitEvent_EndParameterChangeGesture]; printf("end %i\n", mType); }
}
@end

//====================================================================================
static void ParameterListenerDispatcher (void *inRefCon, void *inObject, const AudioUnitParameter *inParameter, Float32 inValue)
{
    [(UDMOctogris_UI *)inRefCon _parameterListener:inObject parameter:inParameter value:inValue];
}

//====================================================================================
@implementation UDMOctogris_UI

- (id)initWithFrame:(NSRect)frame
{
    if ((self = [super initWithFrame:frame]))
	{
		mAU = nil;
		mParameterListener = nil;
		
		mNumberOfSources = 0;
		mNumberOfSpeakers = 0;
	
		[mEnableJoystick setState: NSOffState];
		[mLinkMovement setState: NSOffState];
		[mXYView setLinkMovement:NO];
		
		mSourcesDistanceLabels = [[NSMutableArray alloc] init];
		mSourcesDistanceSliders = [[NSMutableArray alloc] init];
		mOldLinkSourceValue = FALSE;
		mOldLinkMoveValue = FALSE;
		
		mSpeakersAttenuationSliders = [[NSMutableArray alloc] init];
		mSpeakersAttenuationCheckboxes = [[NSMutableArray alloc] init];
		mSpeakersLevels = [[NSMutableArray alloc] init];
		
		mTimer = [[NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(updateLevels) userInfo:nil repeats:YES] retain];
		mLastShownProcessCounter = -1;
		mLevelAdjustment = 1;
    }
    return self;
}

- (void)setAU:(AudioUnit)inAU
{
	NSLog(@"UDMOCtogris setAU %p\n", inAU);

	[mXYView setAU:inAU];

	mAU = inAU;

	UInt32 dataSize = sizeof(mNumberOfSources);
	NSAssert (	AudioUnitGetProperty (mAU, kNumberOfSourcesPropertyID, kAudioUnitScope_Global, 0, &mNumberOfSources, &dataSize) == noErr,
				@"[UDMOctogris_UI setAU:] AudioUnitGetProperty() 1");
		
	dataSize = sizeof(mNumberOfSpeakers);
	NSAssert (	AudioUnitGetProperty (mAU, kNumberOfSpeakersPropertyID, kAudioUnitScope_Global, 0, &mNumberOfSpeakers, &dataSize) == noErr,
				@"[UDMOctogris_UI setAU:] AudioUnitGetProperty() 2");

	if (mNumberOfSources <= 1)
	{
		[mMovementLabel setHidden:YES];
		[mMovementPopUp setHidden:YES];
	}
	else if (mNumberOfSources > 2)
	{
		[mMovementPopUp removeItemAtIndex:1];
		[mMovementPopUp removeItemAtIndex:1];	
		[mMovementPopUp removeItemAtIndex:1];
	}

	for (int i = 0; i < mNumberOfSources; i++)
		[mShowLevelsPopUp addItemWithTitle:[NSString stringWithFormat:@"Source %i", i+1]];
	[mShowLevelsPopUp addItemWithTitle:@"All sources"];
	
	BOOL calculateLevels = YES;
	dataSize = sizeof(calculateLevels);
	NSAssert (	AudioUnitSetProperty (mAU, kCalculateLevelsPropertyID, kAudioUnitScope_Global, 0, &calculateLevels, dataSize) == noErr,
				@"[UDMOctogris_UI setAU:] setAU() levels");
	
	[self populateUI];
}

- (oneway void)release;
{
	int rc = [self retainCount];
	
	[super release];
	rc--;
	
	if (rc == 1)
	{
		// only the timer is still retaining us
		// stop it, release it to break the reference circle
		[mTimer invalidate];
		[mTimer release];
	}
}

- (void) dealloc
{
	NSLog(@"UDMOctogris_UI released");
	
	BOOL calculateLevels = NO;
	UInt32 dataSize = sizeof(calculateLevels);
	ComponentResult err = AudioUnitSetProperty (mAU, kCalculateLevelsPropertyID, kAudioUnitScope_Global, 0, &calculateLevels, dataSize);
	NSLog(@"AudioUnitSetProperty returns: %i", err);
	
	NSAssert (	err == noErr,
				@"[UDMOctogris_UI dealloc:] setAU() levels");

	[self _removeListeners];
	
	[mSourcesDistanceLabels release];
	[mSourcesDistanceSliders release];
	
	[mSpeakersAttenuationSliders release];
	[mSpeakersAttenuationCheckboxes release];
	[mSpeakersLevels release];
	
	[super dealloc];
}

- (void) scrollToTop;
{
	NSRect noScrollBox = [mSpeakersAttenuation frame];
//	NSRect noScrollBox = NSMakeRect(0, -1900, 326, 398);
	mAttenuationScroller = [mSpeakersAttenuation enclosingScrollView];
	
	
	if(mNumberOfSpeakers <= 15)
	{
		[mAttenuationScroller setHasVerticalScroller:NO];
		//[mSpeakersAttenuation sizeToFit];
		noScrollBox.origin.y = -1900.0;
		noScrollBox.size.height = 398.0;
		[mSpeakersAttenuation setFrameFromContentFrame:noScrollBox];
		mAttenBoxHeight = 398.0;
	}
	else {
		//mAttenBoxHeight = NSBOX_HEIGHT;
		mAttenBoxHeight = mNumberOfSpeakers * 23 + 65;
		
		noScrollBox.origin.y = -1900.0;
		noScrollBox.size.height = mAttenBoxHeight;
		[mSpeakersAttenuation setFrameFromContentFrame:noScrollBox];
	}

	
    NSPoint newScrollOrigin;
    // assume that the scrollview is an existing variable
	newScrollOrigin=NSMakePoint(0.0,mAttenBoxHeight);
	
	[mSpeakersAttenuation scrollPoint:newScrollOrigin];
	
	//*** And the same thing again for the distance scroller
	
	NSRect noScrollBox2 = [mSourcesDistance frame];
	//	NSRect noScrollBox = NSMakeRect(0, -1900, 326, 398);
	mDistanceScroller = [mSourcesDistance enclosingScrollView];
	
	
	if(mNumberOfSources <= 11)
	{
		[mDistanceScroller setHasVerticalScroller:NO];
		//[mSpeakersAttenuation sizeToFit];
		noScrollBox2.origin.y = -1900.0;
		noScrollBox2.size.height = 298.0;
		[mSourcesDistance setFrameFromContentFrame:noScrollBox2];
		mDistBoxHeight = 298.0;
	}
	else {
		//mDistBoxHeight = NSBOX_HEIGHT;
		//mDistBoxHeight = 0;
		mDistBoxHeight = mNumberOfSpeakers * 23 + 71;
		
		noScrollBox2.origin.y = -1900.0;
		noScrollBox2.size.height = mDistBoxHeight;
		[mSourcesDistance setFrameFromContentFrame:noScrollBox2];
	}
	
	
    NSPoint newScrollOrigin2;
    // assume that the scrollview is an existing variable
	newScrollOrigin2=NSMakePoint(0.0,mDistBoxHeight);
	
	[mSourcesDistance scrollPoint:newScrollOrigin2];
}

- (void) populateUI
{	
	[mLevelAttack setMinValue:kLevelAttackMin];
	[mLevelAttack setMaxValue:kLevelAttackMax];
		
	[mLevelRelease setMinValue:kLevelReleaseMin];
	[mLevelRelease setMaxValue:kLevelReleaseMax];
		
	[mParamSmooth setMinValue:kParamSmoothMin];
	[mParamSmooth setMaxValue:kParamSmoothMax];
	
	//NOTE: This means that the joystick enable button is always re-initialized to "off" when the window is opened.
	[mEnableJoystick setState: NSOffState];
	[mLinkMovement setState: NSOffState];
	
	[self scrollToTop];

	const int offset1 = 40;
	for (int i = 0; i < mNumberOfSources; i++)
	{
		NSRect labelRect = NSMakeRect(15, mDistBoxHeight - 31 - offset1 - i * 23, 34, 17);
		NSRect sliderRect = NSMakeRect(49, mDistBoxHeight - 31 - offset1 - i * 23, 101, 21);
	
		NSTextField *t = [[[NSTextField alloc] initWithFrame:labelRect] autorelease];
		[t setStringValue:[NSString stringWithFormat:@"%i:", i + 1]];
		[t setEditable:NO];
		[t setSelectable:NO];
		[t setBordered:NO];
		[t setDrawsBackground:NO];
		[mSourcesDistance addSubview:t];
		[mSourcesDistanceLabels addObject:t];
		
		UDMOctogris_Slider *s = [[[UDMOctogris_Slider alloc] initWithFrame:sliderRect] autorelease];
		[s setMinValue:kSourceMinDistance];
		[s setMaxValue:kSourceMaxDistance];
		[s setTarget:self];
		[s setAction:@selector(sourceDistanceChanged:)];
		[s setUI:self type:0];
		[mSourcesDistance addSubview:s];
		[mSourcesDistanceSliders addObject:s];
	}
	
	const int offset2 = 0;
	for (int i = 0; i < mNumberOfSpeakers; i++)
	{
		NSRect checkboxRect = NSMakeRect(16, mAttenBoxHeight - 47 - 18 - offset2 - i * 23, 53, 18);
		NSRect sliderRect = NSMakeRect(73, mAttenBoxHeight - 47 - 21 - offset2 - i * 23, 134, 21);
		NSRect levelRect = NSMakeRect(210, mAttenBoxHeight - 47 - 15 - offset2 - i * 23, 87, 15);
	
		NSButton *b = [[[NSButton alloc] initWithFrame:checkboxRect] autorelease];
		[b setTitle:[NSString stringWithFormat:@"%i:", i + 1]];
		[b setButtonType:NSSwitchButton];
		[b setTarget:self];
		[b setAction:@selector(speakerMuteChanged:)];
		[mSpeakersAttenuation addSubview:b];
		[mSpeakersAttenuationCheckboxes addObject:b];
		
		UDMOctogris_Slider *s = [[[UDMOctogris_Slider alloc] initWithFrame:sliderRect] autorelease];
		[s setMinValue:kSpeakerMinAttenuation];
		[s setMaxValue:kSpeakerMaxAttenuation];
		[s setTarget:self];
		[s setAction:@selector(speakerAttenuationChanged:)];
		[s setUI:self type:1];
		[mSpeakersAttenuation addSubview:s];
		[mSpeakersAttenuationSliders addObject:s];
		
		UDMOctogris_LevelView *l = [[[UDMOctogris_LevelView alloc] initWithFrame:levelRect] autorelease];
		[mSpeakersAttenuation addSubview:l];
		[mSpeakersLevels addObject:l];
	}
	
	[self _addListeners];
	[self _synchronizeUIWithParameterValues];
}



//====================================================================================
- (IBAction) linkSourcesChanged:(id)sender
{
	//VERY ANNOYING: this only kicks in for mouse control, not for automation etc....
	BOOL linkSources = [mLinkSourcesDistances state] == NSOnState;
	float v = linkSources ? 1 : 0;
	AudioUnitParameterID param = kLinkSourcesParam;
	
	[self notifyEventType:kAudioUnitEvent_BeginParameterChangeGesture forParameterID:param];
	NSAssert (	AudioUnitSetParameter(mAU, param, kAudioUnitScope_Global, 0, v, 0) == noErr,
				@"[UDMOctogris_UI linkSourcesChanged] (x.1)");
	
	[mXYView setLinkDistance:linkSources];
				
	[self notifyEventType:kAudioUnitEvent_ParameterValueChange forParameterID:param];
	[self notifyEventType:kAudioUnitEvent_EndParameterChangeGesture forParameterID:param];
}

- (void) linkSourcesChanged2:(BOOL)linkValue
{
	//This is, quite honestly, a bit of a hack to get around the annoying way that the original program was written. 
	//There is no central "entry point" for parameter data, there is some param changing that happens only in the UI
	//which is a pain. This is a function just like the other one above, but is called from the parameter listener.
	[mXYView setLinkDistance:linkValue];
}

- (IBAction) showGridLinesChanged:(id)sender
{
	BOOL showGridLines = [mShowGridLines state] == NSOnState;
	float v = showGridLines ? 1 : 0;
	AudioUnitParameterID param = kShowGridLinesParam;
	
	[self notifyEventType:kAudioUnitEvent_BeginParameterChangeGesture forParameterID:param];
	NSAssert (	AudioUnitSetParameter(mAU, param, kAudioUnitScope_Global, 0, v, 0) == noErr,
				@"[UDMOctogris_UI showGridLinesChanged] (x.1)");
				
	[self notifyEventType:kAudioUnitEvent_ParameterValueChange forParameterID:param];
	[self notifyEventType:kAudioUnitEvent_EndParameterChangeGesture forParameterID:param];
}

- (IBAction) enableJoystickChanged:(id)sender
{
	BOOL enableJoystick = [mEnableJoystick state] == NSOnState;
//	float v = enableJoystick ? 1 : 0;
	[mXYView setEnableJoystick: enableJoystick];
//	AudioUnitParameterID param = kEnableJoystickParam;
//	
//	[self notifyEventType:kAudioUnitEvent_BeginParameterChangeGesture forParameterID:param];
//	NSAssert (	AudioUnitSetParameter(mAU, param, kAudioUnitScope_Global, 0, v, 0) == noErr,
//			  @"[UDMOctogris_UI enableJoystickChanged] (x.1)");
//	
//	[self notifyEventType:kAudioUnitEvent_ParameterValueChange forParameterID:param];
//	[self notifyEventType:kAudioUnitEvent_EndParameterChangeGesture forParameterID:param];
}

- (IBAction) linkMovementChanged:(id)sender
{
	BOOL linkMovement = [mLinkMovement state] == NSOnState;
	float v = linkMovement ? 1 : 0;

	[mXYView setLinkMovement: linkMovement];
	
	//if link movement is ON, disable the movement mode selector
	if(v == 1)
	{
		mDisableModeSelector = TRUE;
		[mMovementPopUp setEnabled:(BOOL)FALSE];
	}
	else {
		mDisableModeSelector = FALSE;
		[mMovementPopUp setEnabled:(BOOL)TRUE];
		
		AudioUnitParameterID param = kLinkMovementParam;
		
		[self notifyEventType:kAudioUnitEvent_BeginParameterChangeGesture forParameterID:param];
		NSAssert (	AudioUnitSetParameter(mAU, param, kAudioUnitScope_Global, 0, v, 0) == noErr,
				  @"[UDMOctogris_UI linkMovementChanged] (x.1)");
		
		[self notifyEventType:kAudioUnitEvent_ParameterValueChange forParameterID:param];
		[self notifyEventType:kAudioUnitEvent_EndParameterChangeGesture forParameterID:param];
	}

}

- (void) linkMovementChanged2:(BOOL)linkValue
{
	//This is, quite honestly, a bit of a hack to get around the annoying way that the original program was written. 
	//There is no central "entry point" for parameter data, there is some param changing that happens only in the UI
	//which is a pain. This is a function just like the other one above, but is called from the parameter listener.
	[mXYView setLinkMovement:linkValue];
	
	//if link movement is ON, disable the movement mode selector
	if(linkValue == 1)
	{
		mDisableModeSelector = TRUE;
		[mMovementPopUp setEnabled:(BOOL)FALSE];
	}
	else {
		mDisableModeSelector = FALSE;
		[mMovementPopUp setEnabled:(BOOL)TRUE];
	}
}

- (IBAction) movementModeChanged:(id)sender
{
	if (mDisableModeSelector == FALSE)
	{
	UInt32 movementMode = [mMovementPopUp indexOfSelectedItem];
	float v = movementMode;
	AudioUnitParameterID param = kMovementModeParam;
	
	[self notifyEventType:kAudioUnitEvent_BeginParameterChangeGesture forParameterID:param];
	NSAssert (	AudioUnitSetParameter(mAU, param, kAudioUnitScope_Global, 0, v, 0) == noErr,
				@"[UDMOctogris_UI movementModeChanged] (x.1)");
				
	[self notifyEventType:kAudioUnitEvent_ParameterValueChange forParameterID:param];
	[self notifyEventType:kAudioUnitEvent_EndParameterChangeGesture forParameterID:param];
	}
}

//====================================================================================
- (void)drawRect:(NSRect)rect
{
	
}

//====================================================================================
- (void)_addListeners
{
	NSAssert (	AUListenerCreate(	ParameterListenerDispatcher, self, 
									//CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, 0.100, // 100 ms
									nil, nil, 0,
                                    &mParameterListener	) == noErr,
                @"[UDMOctogris_UI _addListeners] AUListenerCreate()");
	
	for (int i = 0; i < kConstantParameters; i++)
	{
		AudioUnitParameter parameter = { mAU, i, kAudioUnitScope_Global, 0 };
		NSAssert (	AUListenerAddParameter (mParameterListener, NULL, &parameter) == noErr,
					@"[UDMOctogris_UI _addListeners 0] AUListenerAddParameter()");
	}
	
    for (int i = 0; i < mNumberOfSources; i++)
	{
		AudioUnitParameter parameter = { mAU, ParamForSourceD(i), kAudioUnitScope_Global, 0 };
		NSAssert (	AUListenerAddParameter (mParameterListener, NULL, &parameter) == noErr,
					@"[UDMOctogris_UI _addListeners 1] AUListenerAddParameter()");
	}
	
	for (int i = 0; i < mNumberOfSpeakers; i++)
	{
		{
			AudioUnitParameter parameter = { mAU, ParamForSpeakerA(i), kAudioUnitScope_Global, 0 };
			NSAssert (	AUListenerAddParameter (mParameterListener, NULL, &parameter) == noErr,
						@"[UDMOctogris_UI _addListeners 2] AUListenerAddParameter()");
		}
		{
			AudioUnitParameter parameter = { mAU, ParamForSpeakerM(i), kAudioUnitScope_Global, 0 };
			NSAssert (	AUListenerAddParameter (mParameterListener, NULL, &parameter) == noErr,
						@"[UDMOctogris_UI _addListeners 3] AUListenerAddParameter()");
		}
	}
	
	//for link movement param
	{
		int i = kLinkMovementParam;
		AudioUnitParameter parameter = { mAU, i, kAudioUnitScope_Global, 0 };
		NSAssert (	AUListenerAddParameter (mParameterListener, NULL, &parameter) == noErr,
				  @"[UDMOctogris_UI _addListeners 0] AUListenerAddParameter()");
	}
}

- (void)_removeListeners
{
	for (int i = 0; i < kConstantParameters; i++)
	{
		AudioUnitParameter parameter = { mAU, i, kAudioUnitScope_Global, 0 };
		NSAssert (	AUListenerRemoveParameter(mParameterListener, NULL, &parameter) == noErr,
                    @"[UDMOctogris_UI _removeListeners 0] AUListenerRemoveParameter()");
	}
	for (int i = 0; i < mNumberOfSources; i++)
	{
		AudioUnitParameter parameter = { mAU, ParamForSourceD(i), kAudioUnitScope_Global, 0 };
        NSAssert (	AUListenerRemoveParameter(mParameterListener, NULL, &parameter) == noErr,
                    @"[UDMOctogris_UI _removeListeners 1] AUListenerRemoveParameter()");
    }
	for (int i = 0; i < mNumberOfSpeakers; i++)
	{
		{
			AudioUnitParameter parameter = { mAU, ParamForSpeakerA(i), kAudioUnitScope_Global, 0 };
			NSAssert (	AUListenerRemoveParameter (mParameterListener, NULL, &parameter) == noErr,
						@"[UDMOctogris_UI _removeListeners 2] AUListenerAddParameter()");
		}
		{
			AudioUnitParameter parameter = { mAU, ParamForSpeakerM(i), kAudioUnitScope_Global, 0 };
			NSAssert (	AUListenerRemoveParameter (mParameterListener, NULL, &parameter) == noErr,
						@"[UDMOctogris_UI _removeListeners 3] AUListenerAddParameter()");
		}
	}
    
	NSAssert (	AUListenerDispose(mParameterListener) == noErr,
                @"[UDMOctogris_UI _removeListeners] AUListenerDispose()");
}

- (void)_synchronizeUIWithParameterValues
{
	Float32 value;    
	for (int i = 0; i < kConstantParameters; i++)
	{
		AudioUnitParameter parameter = { mAU, i, kAudioUnitScope_Global, 0 };
        NSAssert (	AudioUnitGetParameter(mAU, i, kAudioUnitScope_Global, 0, &value) == noErr,
                    @"[UDMOctogris_UI synchronizeUIWithParameterValues] (x.1)");
        NSAssert (	AUParameterSet (mParameterListener, self, &parameter, value, 0) == noErr,
                    @"[UDMOctogris_UI synchronizeUIWithParameterValues] (x.2)");
        NSAssert (	AUParameterListenerNotify (mParameterListener, self, &parameter) == noErr,
                    @"[UDMOctogris_UI synchronizeUIWithParameterValues] (x.3)");
    }
    for (int i = 0; i < mNumberOfSources; i++)
	{
		AudioUnitParameter parameter = { mAU, ParamForSourceD(i), kAudioUnitScope_Global, 0 };
        NSAssert (	AudioUnitGetParameter(mAU, ParamForSourceD(i), kAudioUnitScope_Global, 0, &value) == noErr,
                    @"[UDMOctogris_UI synchronizeUIWithParameterValues] (x.1)");
        NSAssert (	AUParameterSet (mParameterListener, self, &parameter, value, 0) == noErr,
                    @"[UDMOctogris_UI synchronizeUIWithParameterValues] (x.2)");
        NSAssert (	AUParameterListenerNotify (mParameterListener, self, &parameter) == noErr,
                    @"[UDMOctogris_UI synchronizeUIWithParameterValues] (x.3)");
    }
	for (int i = 0; i < mNumberOfSpeakers; i++)
	{
		{
			AudioUnitParameter parameter = { mAU, ParamForSpeakerA(i), kAudioUnitScope_Global, 0 };
			NSAssert (	AudioUnitGetParameter(mAU, ParamForSpeakerA(i), kAudioUnitScope_Global, 0, &value) == noErr,
						@"[UDMOctogris_UI synchronizeUIWithParameterValues] (x.4)");
			NSAssert (	AUParameterSet (mParameterListener, self, &parameter, value, 0) == noErr,
						@"[UDMOctogris_UI synchronizeUIWithParameterValues] (x.5)");
			NSAssert (	AUParameterListenerNotify (mParameterListener, self, &parameter) == noErr,
						@"[UDMOctogris_UI synchronizeUIWithParameterValues] (x.6)");
		}
		{
			AudioUnitParameter parameter = { mAU, ParamForSpeakerM(i), kAudioUnitScope_Global, 0 };
			NSAssert (	AudioUnitGetParameter(mAU, ParamForSpeakerM(i), kAudioUnitScope_Global, 0, &value) == noErr,
						@"[UDMOctogris_UI synchronizeUIWithParameterValues] (x.7)");
			NSAssert (	AUParameterSet (mParameterListener, self, &parameter, value, 0) == noErr,
						@"[UDMOctogris_UI synchronizeUIWithParameterValues] (x.8)");
			NSAssert (	AUParameterListenerNotify (mParameterListener, self, &parameter) == noErr,
						@"[UDMOctogris_UI synchronizeUIWithParameterValues] (x.9)");
		}
	}
	//for link movement parameter
	{
		int i = kLinkMovementParam;
		AudioUnitParameter parameter = { mAU, i, kAudioUnitScope_Global, 0 };
        NSAssert (	AudioUnitGetParameter(mAU, i, kAudioUnitScope_Global, 0, &value) == noErr,
				  @"[UDMOctogris_UI synchronizeUIWithParameterValues] (x.1)");
        NSAssert (	AUParameterSet (mParameterListener, self, &parameter, value, 0) == noErr,
				  @"[UDMOctogris_UI synchronizeUIWithParameterValues] (x.2)");
        NSAssert (	AUParameterListenerNotify (mParameterListener, self, &parameter) == noErr,
				  @"[UDMOctogris_UI synchronizeUIWithParameterValues] (x.3)");
    }
}

//====================================================================================
- (void)_parameterListener:(void *)inObject parameter:(const AudioUnitParameter *)inParameter value:(Float32)inValue
{
    //inObject ignored in this case
	int param = inParameter->mParameterID;
    //NSLog(@"_parameterListener %i -> %f", param, inValue);
	BOOL boolValue;
	
	if (param == kLinkMovementParam)
	{
		//Schmitt Trigger type thing to avoid bouncing at the threshold of ON or OFF
		if(mOldLinkMoveValue == FALSE)
			if(inValue > 0.55)
				boolValue = TRUE;
			else
				boolValue = FALSE;
			else //mOldLinkMoveValue == TRUE
				if(inValue < 0.45)
					boolValue = FALSE;
				else
					boolValue = TRUE;
		
		if(boolValue != mOldLinkMoveValue)
		{
			[mLinkMovement setState:boolValue ? NSOnState : NSOffState];
			[self linkMovementChanged2:boolValue];
			mOldLinkMoveValue = boolValue ;
		}
		return;
	}
	
	if (param < kConstantParameters)
	{
		float v = inValue;
		
		NSString *s;
		if (v < 1) s = [NSString stringWithFormat:@"%.2f ms", v];
		else if (v < 10) s = [NSString stringWithFormat:@"%.1f ms", v];
		else s = [NSString stringWithFormat:@"%.0f ms", v];

		switch(param)
		{
			/*
			case kLevelAttackParam:
				[mLevelAttack setFloatValue:v];
				[mLevelAttackValue setStringValue:s];
				break;
				
			case kLevelReleaseParam:
				[mLevelRelease setFloatValue:v];
				[mLevelReleaseValue setStringValue:s];
				break;
			*/
				
			case kParamSmoothParam:
				[mParamSmooth setFloatValue:v];
				[mParamSmoothValue setStringValue:s];
				break;
				
				
			case kLinkSourcesParam:
				
				//Schmitt Trigger type thing to avoid bouncing at the threshold of ON or OFF
				if(mOldLinkSourceValue == FALSE)
					if(inValue > 0.55)
						boolValue = TRUE;
					else
						boolValue = FALSE;
				else //mOldLinkSourceValue == TRUE
					if(inValue < 0.45)
						boolValue = FALSE;
					else
						boolValue = TRUE;

				if(boolValue != mOldLinkSourceValue)
				{
					[mLinkSourcesDistances setState:boolValue ? NSOnState : NSOffState];
					[self linkSourcesChanged2:boolValue];
					mOldLinkSourceValue = boolValue ;
				}
				break;
				
			case kShowGridLinesParam:
				[mShowGridLines setState:(inValue < 0.5) ? NSOffState : NSOnState];
				[mXYView setShowGridLines:(inValue < 0.5) ? NO : YES];
				break;
				
			case kMovementModeParam:
				if (mDisableModeSelector == FALSE)
				{
					[mMovementPopUp selectItemAtIndex:(int)(inValue + 0.5)];
					[mXYView setMovementMode:(int)(inValue + 0.5)];
				}
				break;
				
			case kShowLevelsParam:
				[mShowLevelsPopUp selectItemAtIndex:(int)(inValue + 0.5)];
				[mXYView setShowLevels:(int)(inValue + 0.5)];
				break;
				
			case kTakeAttenuationParam:
				[mTakeAttenuation setState:(inValue < 0.5) ? NSOffState : NSOnState];
				[mXYView setTakeAttenuation:(inValue < 0.5) ? NO : YES];
				break;
				
//			case kEnableJoystickParam:
//				[mEnableJoystick setState:(inValue < 0.5) ? NSOffState : NSOnState];
//				[mXYView setEnableJoystick:(inValue < 0.5) ? NO : YES];
//				break;
			
		}
		return;
	}
	
	param -= kConstantParameters;
	if (param < mNumberOfSources * kParamsPerSource)
	{
		int t = param % kParamsPerSource;
		int s = param / kParamsPerSource;
		float dval = 0;
		switch(t)
		{
			case kSourceD:
			{
				NSSlider *sl = [mSourcesDistanceSliders objectAtIndex:s];
				dval = (kSourceMaxDistance+kSourceMinDistance) - inValue;
				[sl setFloatValue:dval];
				
				// if distances linked, update the sliders.
				if(s == 0 && mOldLinkSourceValue == TRUE)
				{
					for (int i = 1; i < mNumberOfSources; i++)
					{
						// for every other source, link this shit up
						NSSlider *sl = [mSourcesDistanceSliders objectAtIndex:i];
						[sl setFloatValue:dval];
					}
				}
			}
			break;
		}
		return;
	}
	
	param -= mNumberOfSources * kParamsPerSource;
	if (param < mNumberOfSpeakers * kParamsPerSpeakers)
	{
		int t = param % kParamsPerSpeakers;
		int s = param / kParamsPerSpeakers;
		switch(t)
		{
			case kSpeakerA:
			{
				NSSlider *sl = [mSpeakersAttenuationSliders objectAtIndex:s];
				[sl setFloatValue:inValue];
			}
			break;
			
			case kSpeakerM:
			{
				NSButton *b = [mSpeakersAttenuationCheckboxes objectAtIndex:s];
				[b setState:(inValue < 0.5) ? NSOffState : NSOnState];
			}
			break;
		}
		return;
	}

	// should never get here
	NSAssert(false, @"[UDMOctogris_UI _parameterListener]");
}


//====================================================================================
- (void) notifyEventType:(AudioUnitEventType)type forParameterID:(AudioUnitParameterID)parameterID
{
	AudioUnitEvent myEvent;
	myEvent.mArgument.mParameter.mAudioUnit = mAU;
	myEvent.mArgument.mParameter.mParameterID = parameterID;
	myEvent.mArgument.mParameter.mScope = kAudioUnitScope_Global;
	myEvent.mArgument.mParameter.mElement = 0;
	myEvent.mEventType = type;
	AUEventListenerNotify(NULL, NULL, &myEvent);
}

//====================================================================================
- (IBAction) sourceDistanceChanged:(id)sender
{
	BOOL link = ([mLinkSourcesDistances state] == NSOnState);
	int c = [mSourcesDistanceSliders count];
	float v = (kSourceMaxDistance+kSourceMinDistance) - [sender floatValue];
	
	if (mSetSliderToDefault==TRUE)
	{
		v = kSourceDefaultDistance;
		mSetSliderToDefault = FALSE;
	}
	
	for (int i = 0; i < c; i++)
	{
		NSSlider *s = [mSourcesDistanceSliders objectAtIndex:i];
		if (s == sender || link)
		{
			AudioUnitParameterID param = ParamForSourceD(i);

			NSAssert (	AudioUnitSetParameter(mAU, param, kAudioUnitScope_Global, 0, v, 0) == noErr,
						@"[UDMOctogris_UI sourceDistanceChanged] (x.1)");
						
			[self notifyEventType:kAudioUnitEvent_ParameterValueChange forParameterID:param];			
			if (!link) return;
		}
	}
}

- (IBAction) speakerMuteChanged:(id)sender
{
	int c = [mSpeakersAttenuationCheckboxes count];
	for (int i = 0; i < c; i++)
	{
		NSButton *b = [mSpeakersAttenuationCheckboxes objectAtIndex:i];
		if (b == sender)
		{
			float v = [b intValue];
			AudioUnitParameterID param = ParamForSpeakerM(i);
			
			[self notifyEventType:kAudioUnitEvent_BeginParameterChangeGesture forParameterID:param];
			NSAssert (	AudioUnitSetParameter(mAU, param, kAudioUnitScope_Global, 0, v, 0) == noErr,
						@"[UDMOctogris_UI speakerMuteChanged] (x.1)");
						
			[self notifyEventType:kAudioUnitEvent_ParameterValueChange forParameterID:param];
			[self notifyEventType:kAudioUnitEvent_EndParameterChangeGesture forParameterID:param];
			return;
		}
	}
}

- (IBAction) speakerAttenuationChanged:(id)sender
{
	int c = [mSpeakersAttenuationSliders count];
	for (int i = 0; i < c; i++)
	{
		NSSlider *s = [mSpeakersAttenuationSliders objectAtIndex:i];
		if (s == sender)
		{
			float v = [s floatValue];
			
			if (mSetSliderToDefault==TRUE)
			{
				v = kSpeakerDefaultAttenuation;
				mSetSliderToDefault = FALSE;
			}
			
			AudioUnitParameterID param = ParamForSpeakerA(i);
			
			NSAssert (	AudioUnitSetParameter(mAU, param, kAudioUnitScope_Global, 0, v, 0) == noErr,
						@"[UDMOctogris_UI speakerAttenuationChanged] (x.1)");
						
			[self notifyEventType:kAudioUnitEvent_ParameterValueChange forParameterID:param];
			return;
		}
	}
}

- (void) sliderNotify:(id)sender type:(int)t event:(AudioUnitEventType)e
{
	if (t == 0)
	{
		BOOL link = ([mLinkSourcesDistances state] == NSOnState);
		int c = [mSourcesDistanceSliders count];
		for (int i = 0; i < c; i++)
		{
			NSSlider *s = [mSourcesDistanceSliders objectAtIndex:i];
			if (s == sender || link)
			{
				[self notifyEventType:e forParameterID:ParamForSourceD(i)];
				if (!link) return;
			}
		}
	}
	else if (t == 1)
	{
		int c = [mSpeakersAttenuationSliders count];
		for (int i = 0; i < c; i++)
		{
			NSSlider *s = [mSpeakersAttenuationSliders objectAtIndex:i];
			if (s == sender)
			{
				[self notifyEventType:e forParameterID:ParamForSpeakerA(i)];
				return;
			}
		}
	}
}

- (void) updateLevels
{
	float levels[mNumberOfSpeakers];
	UInt32 dataSize = sizeof(levels);
	
	ComponentResult cr = AudioUnitGetProperty (mAU, kLevelsPropertyID, kAudioUnitScope_Global, 0, levels, &dataSize);
	if (cr != noErr) return;
	
	int64 processCounter;
	dataSize = sizeof(processCounter);
	cr = AudioUnitGetProperty (mAU, kProcessCounterPropertyID, kAudioUnitScope_Global, 0, &processCounter, &dataSize);
	if (cr != noErr) return;
	
	if (processCounter != mLastShownProcessCounter)
	{
		mLastShownProcessCounter = processCounter;
		mLevelAdjustment = 1;
	}
	else
		mLevelAdjustment *= 0.8;
										
	for (int i = 0; i < mNumberOfSpeakers; i++)
	{
		UDMOctogris_LevelView *lv = [mSpeakersLevels objectAtIndex:i];
		[lv setLevel:levels[i] * mLevelAdjustment];
	}
}

- (IBAction) levelAttackChanged:(id)sender
{
/*
	float v = [sender floatValue];
	AudioUnitParameterID param = kLevelAttackParam;
			
	NSAssert (	AudioUnitSetParameter(mAU, param, kAudioUnitScope_Global, 0, v, 0) == noErr,
				@"[UDMOctogris_UI levelAttackChanged] (x.1)");				
	[self notifyEventType:kAudioUnitEvent_ParameterValueChange forParameterID:param];
	
	NSString *s;
	if (v < 1) s = [NSString stringWithFormat:@"%.2f ms", v];
	else if (v < 10) s = [NSString stringWithFormat:@"%.1f ms", v];
	else s = [NSString stringWithFormat:@"%.0f ms", v];
	
	[mLevelAttackValue setStringValue:s];
*/
}

- (IBAction) levelReleaseChanged:(id)sender
{
/*
	float v = [sender floatValue];
	AudioUnitParameterID param = kLevelReleaseParam;
			
	NSAssert (	AudioUnitSetParameter(mAU, param, kAudioUnitScope_Global, 0, v, 0) == noErr,
				@"[UDMOctogris_UI levelReleaseChanged] (x.1)");				
	[self notifyEventType:kAudioUnitEvent_ParameterValueChange forParameterID:param];
	
	NSString *s;
	if (v < 1) s = [NSString stringWithFormat:@"%.2f ms", v];
	else if (v < 10) s = [NSString stringWithFormat:@"%.1f ms", v];
	else s = [NSString stringWithFormat:@"%.0f ms", v];
	
	[mLevelReleaseValue setStringValue:s];
*/
}

- (IBAction) paramSmoothChanged:(id)sender
{
	float v = [sender floatValue];
	AudioUnitParameterID param = kParamSmoothParam;
			
	NSAssert (	AudioUnitSetParameter(mAU, param, kAudioUnitScope_Global, 0, v, 0) == noErr,
				@"[UDMOctogris_UI paramSmoothValue] (x.1)");				
	[self notifyEventType:kAudioUnitEvent_ParameterValueChange forParameterID:param];
	
	NSString *s;
	if (v < 1) s = [NSString stringWithFormat:@"%.2f ms", v];
	else if (v < 10) s = [NSString stringWithFormat:@"%.1f ms", v];
	else s = [NSString stringWithFormat:@"%.0f ms", v];
	
	[mParamSmoothValue setStringValue:s];
}

- (IBAction) showLevelsChanged:(id)sender
{
	UInt32 showLevels = [mShowLevelsPopUp indexOfSelectedItem];
	float v = showLevels;
	AudioUnitParameterID param = kShowLevelsParam;
	
	[self notifyEventType:kAudioUnitEvent_BeginParameterChangeGesture forParameterID:param];
	NSAssert (	AudioUnitSetParameter(mAU, param, kAudioUnitScope_Global, 0, v, 0) == noErr,
				@"[UDMOctogris_UI showLevelsChanged] (x.1)");
				
	[self notifyEventType:kAudioUnitEvent_ParameterValueChange forParameterID:param];
	[self notifyEventType:kAudioUnitEvent_EndParameterChangeGesture forParameterID:param];

}

- (IBAction) takeAttenuationChanged:(id)sender
{
	BOOL takeAttenuation = [mTakeAttenuation state] == NSOnState;
	float v = takeAttenuation ? 1 : 0;
	AudioUnitParameterID param = kTakeAttenuationParam;
	
	[self notifyEventType:kAudioUnitEvent_BeginParameterChangeGesture forParameterID:param];
	NSAssert (	AudioUnitSetParameter(mAU, param, kAudioUnitScope_Global, 0, v, 0) == noErr,
				@"[UDMOctogris_UI takeAttenuationChanged] (x.1)");
				
	[self notifyEventType:kAudioUnitEvent_ParameterValueChange forParameterID:param];
	[self notifyEventType:kAudioUnitEvent_EndParameterChangeGesture forParameterID:param];
}

@end
